<?php
// +--------------------------------------------------------------------------------------------------------------------
// | 本文件是《FastChat》的一部分，所有代码、文字、图片、样式、风格等版权归作者所有，如有复制、仿冒、抄袭、盗用，FastAdmin和作者将追究法律责任
// +--------------------------------------------------------------------------------------------------------------------
// | @作者: 妙码生花 <1094963513@qq.com>
// +--------------------------------------------------------------------------------------------------------------------
// | @唯一授权链接: https://www.fastadmin.net/store/fastchat.html
// +--------------------------------------------------------------------------------------------------------------------

/**
 * 检测业务代码死循环或者长时间阻塞等问题
 * 如果发现业务卡死，可以将下面 declare 打开（去掉//注释），并执行 php think fastchat reload
 * 然后观察一段时间 ../../vendor/workerman/workerman.log (默认日志位置) 看是否有 process_timeout 异常
 */

// declare(ticks=1);
namespace addons\fastchat\library\GatewayWorker\Applications\FastChat;

use think\Db;
use fast\Random;
use think\Config;
use Workerman\Lib\Timer;
use GatewayWorker\Lib\Gateway;
use addons\fastchat\library\Common;

/**
 * 主逻辑
 * 主要是处理 onConnect onMessage onClose 三个方法
 * onConnect 和 onClose 如果不需要可以不用实现并删除
 */
class Events
{

    public static function onWorkerStart()
    {
        Config::set('database.break_reconnect', true);
        Db::connect();
    }

    /**
     * WebSocket 链接成功
     */
    public static function onWebSocketConnect($client_id, $data)
    {
        // 安全检查
        array_walk_recursive($data, array('addons\fastchat\library\Common', 'check_variable'));

        $config_data = get_addon_config('fastchat');

        $_SESSION['origin'] = 'http://' . $data['server']['SERVER_NAME'];//设置服务器域名

        if (!$config_data['anonymous_to_admin'] && !$config_data['anonymous_to_user'] && !$data['get']['token']) {
            // 禁止匿名发起会话,确认用户收到登录提示后,直接关闭链接
            Gateway::sendToClient($client_id, json_encode(['msgtype' => 'clear', 'code' => 401]));

            return;
        }

        // 获取连接人信息

        $token_info = false;

        if ($data['get']['modulename'] == 'admin' && $data['get']['token']) {
            // 验证管理员身份
            $token_info = Common::check_admin($data['get']['token']);

            // 设置定时器，定时检测管理员身份是否过期
            $_SESSION['auth_timer_id'] = Timer::add(30, function ($client_id, $token) {
                $token_info = Common::check_admin($token);
                if (!$token_info) {
                    Gateway::closeClient($client_id);
                }
            }, array($client_id, $data['get']['token']));

        } elseif ($data['get']['modulename'] != 'admin' && $data['get']['token']) {
            // 验证用户身份
            $token_info = Common::check_fa_user($data['get']['token'], 0);

            if ($token_info && $data['get']['fastchat_user']) {
                // 游客登录了
                Common::tourists_to_login($data['get']['fastchat_user'], $token_info['user_id']);
            }
        } elseif ($data['get']['modulename'] != 'admin' && $data['get']['fastchat_user']) {
            // 验证游客身份
            $token_info = Common::check_tourists($data['get']['fastchat_user']);

            if ($token_info && !$config_data['anonymous_to_admin'] && !$config_data['anonymous_to_user']) {
                // 禁止匿名发起会话,确认用户收到登录提示后,直接关闭链接
                Gateway::sendToClient($client_id, json_encode(['msgtype' => 'clear', 'code' => 401]));
            }
        }

        if ($token_info) {
            Gateway::bindUid($client_id, $token_info['user_id']);
            $_SESSION['user_id'] = $token_info['user_id'];
        } elseif ($data['get']['modulename'] != 'admin') {
            // 建立游客身份,cookie值发送到前台保存->已废弃,转由http处理
            $tourists_max_id = Db::name('fastchat_tourists')->max('id');

            $token = Random::uuid();

            $tourists = [
                'avatar'     => '',// 随机头像->算了算了
                'nickname'   => '游客 ' . $tourists_max_id,
                'token'      => $token,
                'createtime' => time()
            ];

            if (Db::name('fastchat_tourists')->insert($tourists)) {

                $tourists_id = Db::name('fastchat_tourists')->getLastInsID();

                $_SESSION['user_id'] = $tourists_id . '||tourists';
                Gateway::bindUid($client_id, $_SESSION['user_id']);

                $keeptime   = 864000;
                $expiretime = time() + $keeptime;
                $key        = md5(md5($tourists_id) . md5($keeptime) . md5($expiretime) . $token);
                $data       = [$tourists_id, $keeptime, $expiretime, $key];

                // 为游客登录
                $fastchat_tourists   = implode('|', $data);
                $token_info          = Common::check_tourists($fastchat_tourists);
                $_SESSION['user_id'] = $token_info['user_id'];

                Gateway::sendToClient($client_id, json_encode([
                    'msgtype' => 'welcome_tourists',
                    'code'    => 1,
                    'data'    => $fastchat_tourists
                ]));
            } else {
                Gateway::sendToClient($client_id, json_encode([
                    'msgtype' => 'welcome_tourists',
                    'code'    => 0,
                    'msg'     => '会话服务异常！'
                ]));
            }

        } else {
            $_SESSION['user_id'] = $client_id;
        }

        $config_data['new_msg'] = Common::get_unread_messages($_SESSION['user_id'], false);

        $config_data['user_info'] = $token_info;

        // 向当前client_id发送数据
        Gateway::sendToClient($client_id, json_encode(['msgtype' => 'config', 'data' => $config_data]));
        // 向所有人发送
        Gateway::sendToAll(json_encode([
            'msgtype' => 'online',
            'user_id' => $_SESSION['user_id']
        ]));
    }

    /**
     * 当客户端发来消息时触发
     * @param int   $client_id 连接id
     * @param mixed $message   具体消息
     * @return void
     */
    public static function onMessage($client_id, $message)
    {
        // 分发到控制器
        $data = json_decode($message, true);

        // 安全检查
        array_walk_recursive($data, array('addons\fastchat\library\Common', 'check_variable'));

        if (!is_array($data) || !isset($data['c']) || !isset($data['a'])) {

            $res_data = array('msgtype' => 'notice', 'msg' => '错误的请求！');
            Gateway::sendToClient($client_id, json_encode($res_data));
            return;
        }

        if ($data['c'] == 'clear') {
            Gateway::closeClient($client_id);
            return;
        }

        $filename = __DIR__ . '/controller/' . $data['c'] . '.php';//载入文件类似/controller/index.php

        if (file_exists($filename)) {

            require_once($filename);

            // 检查要访问的类是否存在
            if (!class_exists($data['c'], false)) {
                $res_data = array('msgtype' => 'notice', 'msg' => '您访问的控制器不存在！');
                Gateway::sendToClient($client_id, json_encode($res_data));
                return;
            }
        } else {

            $res_data = array('msgtype' => 'notice', 'msg' => '您访问的页面并存在！');
            Gateway::sendToClient($client_id, json_encode($res_data));
            return;
        }

        $o = new $data['c'](); // 新建对象

        if (!method_exists($o, $data['a'])) {
            Gateway::sendToClient($client_id, json_encode(['msgtype' => 'notice', 'msg' => '您访问的方法并存在！']));
            return;
        }

        $data['data'] = $data['data'] ?? '';

        // 调用对象$o($c)里的方法$a
        call_user_func_array(array($o, $data['a']), array($client_id, $data['data']));
    }

    /**
     * 当用户断开连接时触发
     */
    public static function onClose()
    {
        if (isset($_SESSION['auth_timer_id'])) {
            Timer::del($_SESSION['auth_timer_id']);
        }

        // 向所有人发送
        if (isset($_SESSION['user_id'])) {

            $user_info = Common::user_info($_SESSION['user_id']);

            if (($user_info['session_type'] != 5) || ($user_info['online_status'] == 1)) {
                Gateway::sendToAll(json_encode([
                    'msgtype' => 'offline',
                    'user_id' => $_SESSION['user_id']
                ]));
            }

        }

    }

}
